import Vue from 'vue'
const osName = Vue.prototype.$sysInfo.osName;
const crc = require('./crc32');
const littleEndianUtils = require('./little_endian_utils');
let isSetMtu = false;

const BASE_POS = 3;
const CALCULATE_CHECKSUM_RESPONSE_FIELD = {
	OFFSET: BASE_POS + 0,
	CRC32: BASE_POS + 4,
};
const SELECT_RESPONSE_FIELD = {
	MAXIMUM_SIZE: BASE_POS + 0,
	OFFSET: BASE_POS + 4,
	CRC32: BASE_POS + 8,
};
/**
 * notify 00002AF0-0000-1000-8000-00805F9B34FB
 * write  00002AF1-0000-1000-8000-00805F9B34FB
 */
class BleUtils {
	#controlPointCharacteristicUUID = '8EC90001-F315-4F60-9FB8-838830DAEA50';
	#packetCharacteristicUUID = '8EC90002-F315-4F60-9FB8-838830DAEA50';
	constructor(serviceUUID = '6E400001-B5A3-F393-E0A9-E50E24DCCA9E', writeUUID =
		'6E400002-B5A3-F393-E0A9-E50E24DCCA9E', notifyId = '6E400003-B5A3-F393-E0A9-E50E24DCCA9E') {
		this.isDFUUpgrade = false;
		this.deviceId = '';
		this.writeUUID = writeUUID;
		this.notifyUUID = notifyId;
		this.serviceUUID = serviceUUID;
		this.serviceId = serviceUUID.toUpperCase(); // 设备的 serviceId
		this.characteristicId = writeUUID.toUpperCase(); // 设备的特征值
		this.notifyId = notifyId.toUpperCase();
		this.characteristicId90001 = this.#controlPointCharacteristicUUID;
		this.characteristicId90002 = this.#packetCharacteristicUUID;
	}

	isLocationEnabled() {
		return new Promise((resolve, reject) => {
			uni.getSystemInfo({
				success: (res) => {
					resolve({
						locationEnabled: res.locationEnabled,
						locationAuthorized: res.locationAuthorized
					})
				},
				fail: (res) => {
					reject({
						errCode: 99990
					})
				}
			})
		})
	}
	// 授权小程序使用定位服务
	doAuthorize(scopeStr) {
		return new Promise((resolve, reject) => {
			uni.authorize({
				scope: scopeStr,
				success: () => {
					resolve();
				},
				fail: (res) => {
					console.log('doAuthorize err', err)
					reject({
						errCode: 99991
					})
				}
			})
		})
	}
	// 打开蓝牙设备器
	openBluetoothAdapter() {
		return new Promise((resolve, reject) => {
			uni.openBluetoothAdapter({
				mode: 'central',
				success: () => {
					resolve();
				},
				fail: (res) => {
					console.log('openBluetoothAdapter fail--->', res)
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}

	// 获取蓝牙适配器状态
	getBluetoothAdapterState() {
		return new Promise((resolve, reject) => {
			uni.getBluetoothAdapterState({
				success: (res) => {
					resolve(res)
				},
				fail: (res) => {
					console.log('getBluetoothAdapterState', res)
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}

	// 监听设备然后开启设备搜索 120秒钟如果还未发现设备 就停止扫描并reject返回失败
	onBluetoothDeviceFoundAndStartDiscovery(deviceName, duration = 120000) {
		let searchTimer = null; // 搜索timer
		return new Promise((resolve, reject) => {
			// 先监听
			uni.onBluetoothDeviceFound((res) => {
				const device = res.devices[0];
				if (!device.name && !device.localName) {
					return;
				}
				if (this.isDFUUpgrade) {
					const bleName = device.name.toLocaleUpperCase();
					if (bleName.indexOf("DFU") != -1) {
						// console.log('找到DFU设备啦！！！');
						if (searchTimer) clearTimeout(searchTimer);
						this.stopBluetoothDevicesDiscovery();
						resolve(device);
					}
				} else if (device.localName == deviceName) {
					console.log('found device ==>', JSON.stringify(device));
					if (searchTimer) clearTimeout(searchTimer);
					// 关闭搜索
					this.stopBluetoothDevicesDiscovery();
					resolve(device)
				}
			})
			// 然后开启搜索
			uni.startBluetoothDevicesDiscovery({
				allowDuplicatesKey: false,
				success: (res) => {
					searchTimer = setTimeout(() => {
						if (searchTimer) clearTimeout(searchTimer);
						this.stopBluetoothDevicesDiscovery();
						reject({
							errCode: 10002
						})
					}, duration)
				},
				fail: (res) => {
					if (searchTimer) clearTimeout(searchTimer);
					this.stopBluetoothDevicesDiscovery();
					console.error('startBluetoothDevicesDiscovery err-->', res)
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}

	// 创建连接
	createBLEConnection(deviceId) {
		this.deviceId = deviceId;
		return new Promise((resolve, reject) => {
			uni.createBLEConnection({
				deviceId,
				timeout: 6000, // 连接超时时间 ms
				success: () => {
					console.log('蓝牙连接成功')
					resolve();
				},
				fail: (res) => {
					console.log('createBLEConnection err--->', res)
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}
	// 修改mtu
	setBLEMTU() {
		return new Promise((resolve, reject) => {
			uni.setBLEMTU({
				deviceId: this.deviceId,
				mtu: 120, // 120个字节
				success() {
					resolve();
				},
				fail(res) {
					console.log('setBLEMTU err new mtu res is', res)
					resolve(res.mtu)
				}
			})
		})
	}
	// 获取蓝牙低功耗的最大传输单元
	getBLEMTU() {
		return new Promise((resolve, reject) => {
			console.log('getBLEMTU--->', this.deviceId)
			uni.getBLEMTU({
				deviceId: this.deviceId,
				writeType: 'write',
				success(mtu) {
					resolve(mtu)
				},
				fail(err) {
					console.log('getBLEMTU err--->', res)
					reject({
						errCode: '99999'
					})
				}
			})
		})
	}
	// 获取连接的蓝牙设备所有服务
	getBLEDeviceServices() {
		return new Promise((resolve, reject) => {
			uni.getBLEDeviceServices({
				deviceId: this.deviceId,
				success: (res) => {
					const services = res.services;
					if (!this.isDFUUpgrade) {
						for (let i = 0; i < services.length; i++) {
							const service = services[i];
							if (service.isPrimary) {
								this.serviceId = service.uuid;
								break;
							}
						}
						resolve();
					} else {
						for (let i = 0; i < services.length; i++) {
							const service = services[i];
							// console.log('found service is ', JSON.stringify(service));
							const uuid = service.uuid;
							if (uuid.indexOf('FE59-') != -1) {
								this.serviceId = uuid;
								break;
							}
						}
						// console.log('DFUUpgrade DFU serviceId is ', this.serviceId);
						if (this.serviceId) {
							resolve();
						} else {
							reject({
								errCode: 99992
							})
						}
					}
				},
				fail: (res) => {
					console.log('getBLEDeviceServices err', res)
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}
	// 获取蓝牙设备特征值
	getBLEDeviceCharacteristics() {
		return new Promise((resolve, reject) => {
			uni.getBLEDeviceCharacteristics({
				deviceId: this.deviceId,
				serviceId: this.serviceId,
				success: async (res) => {
					if (this.isDFUUpgrade) {
						for (let i in res.characteristics) {
							const c = res.characteristics[i];
							if (c.uuid == this.#controlPointCharacteristicUUID) {
								this.notifyId = this.#controlPointCharacteristicUUID;
							}
							if (c.uuid == this.#packetCharacteristicUUID) {
								this.characteristicId = this.#packetCharacteristicUUID;
							}
						}
						console.log('DFU notify id ', this.notifyId, ' write id ', this
							.characteristicId)
					}
					resolve();
				},
				fail: (res) => {
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}

	// 启用特征值变化监听
	notifyBLECharacteristicValueChange(deviceId) {
		console.log('notify params-->', 'deviceId-->', deviceId, 'serviceId-->', this.serviceId, 'notifyId-->', this
			.notifyId)
		return new Promise((resolve, reject) => {
			uni.notifyBLECharacteristicValueChange({
				deviceId,
				serviceId: this.serviceId,
				characteristicId: this.notifyId,
				state: true,
				success: (res) => {
					resolve();
				},
				fail: (res) => {
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}
	// 收到蓝牙返回的数据
	onBLECharacteristicValueChange(fn) {
		uni.onBLECharacteristicValueChange((res) => {
			const str = this.ab2hex(res.value);
			fn(str);
		})
	}

	/**
	 * 自定义方法  针对不同平台连接蓝牙并监听

	 * @param {Object} bleName
	 * @param {Object} mac
	 */
	waitBleConnAndNotify(bleName, mac, isDFUUpgrade = false) {
		console.log('bleName===>', bleName)
		console.log('mac===>', mac)
		this.isDFUUpgrade = isDFUUpgrade;
		return new Promise(async (resolve, reject) => {
			if (isDFUUpgrade) {
				this.characteristicId = this.#packetCharacteristicUUID;
				this.notifyId = this.#controlPointCharacteristicUUID;
			} else {
				this.characteristicId = this.writeUUID;
				this.notifyId = this.notifyUUID;
				this.serviceId = this.serviceUUID;
			}
			try {
				if (this.isDFUUpgrade) {
					const device = await this.onBluetoothDeviceFoundAndStartDiscovery(bleName);
					console.log('DFU device is ', JSON.stringify(device))
					this.deviceId = device.deviceId;
					await this.createBLEConnection(this.deviceId);
					// 获取设备的服务
					console.log('------get ble service start------')
					await this.getBLEDeviceServices();
					// 获取蓝牙设备的服务的所有特征值
					console.log('------get ble characteristics start------')
					await this.getBLEDeviceCharacteristics();
					// 启动蓝牙特征值监听
					console.log('------set DFU ble notify characteristics start------')
					await this.notifyBLECharacteristicValueChange(this.deviceId);
					console.log('------set DFU ble notify characteristics success------')
					resolve()
				} else {
					if (osName == 'ios') {
						const device = await this.onBluetoothDeviceFoundAndStartDiscovery(bleName);
						const uuid = device.deviceId
						await this.createBLEConnection(uuid);
						// 获取设备的服务
						await this.getBLEDeviceServices();
						// 获取蓝牙设备的服务的所有特征值
						await this.getBLEDeviceCharacteristics();
						// 启动蓝牙特征值监听
						await this.notifyBLECharacteristicValueChange(uuid);
						resolve()
					} else {
						const device = await this.onBluetoothDeviceFoundAndStartDiscovery(bleName);
						const uuid = device.deviceId
						await this.createBLEConnection(uuid);
						// await this.createBLEConnection(bleName);
						// 启动蓝牙特征值监听
						console.log('------set ble notify characteristics start------')
						await this.notifyBLECharacteristicValueChange(mac);
						resolve()
					}
				}
			} catch (e) {
				reject(e)
			}
		})
	}

	// 向蓝牙写入数据
	writeBLECharacteristicValue(data) {
		// const value = this.hexToBytes(str);
		return new Promise((resolve, reject) => {
			uni.writeBLECharacteristicValue({
				deviceId: this.deviceId,
				serviceId: this.serviceId,
				characteristicId: this.characteristicId,
				value: data,
				success: (res) => {
					resolve(res);
				},
				fail: (res) => {
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}
	// 向蓝牙90001写入数据
	writeTo90001(buffer) {
		return new Promise((resolve, reject) => {
			uni.writeBLECharacteristicValue({
				deviceId: this.deviceId,
				serviceId: this.serviceId,
				characteristicId: this.characteristicId90001,
				value: buffer,
				success: (res) => {
					// console.log('-----writeTo90001 success-----')
					resolve(res)
				},
				fail: (res) => {
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}
	// 向蓝牙90002写入数据
	writeTo90002(buffer) {
		return new Promise((resolve, reject) => {
			uni.writeBLECharacteristicValue({
				deviceId: this.deviceId,
				serviceId: this.serviceId,
				characteristicId: this.characteristicId90002,
				value: buffer,
				success: (res) => {
					resolve(res)
				},
				fail: (res) => {
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}
	// 停止扫描
	stopBluetoothDevicesDiscovery() {
		return new Promise((resolve, reject) => {
			uni.stopBluetoothDevicesDiscovery({
				success: () => {
					resolve(true);
				},
				fail: (res) => {
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}
	// 断开和蓝牙设备的连接
	closeBLEConnection() {
		return new Promise((resolve, reject) => {
			uni.closeBLEConnection({
				deviceId: this.deviceId,
				success: () => {
					console.log('----close ble connection----')
					resolve();
				},
				fail: (res) => {
					resolve({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}
	// 关闭蓝牙初始化
	closeBluetoothAdapter() {
		return new Promise((resolve, reject) => {
			uni.closeBluetoothAdapter({
				success: () => {
					resolve();
				},
				fail: (res) => {
					reject({
						errCode: res.errCode || res.errno
					})
				}
			})
		})
	}
	// 字符串转指定长度的数组
	strSplitToArr(str, len) {
		let arr = [];
		let index = 0;
		while (index < str.length) {
			arr.push(str.slice(index, index += len));
		}
		return arr;
	}
	myStringtoHex(str) {
		str = str.toLowerCase();
		let newBuffer = new ArrayBuffer(str.length / 2);
		let hexBuffer = new Uint8Array(newBuffer, 0);
		let h = 0,
			l = 0;
		for (let i = 0; i < str.length / 2; i++) {
			h = str.charCodeAt(2 * i);
			l = str.charCodeAt(2 * i + 1);
			if (48 <= h && h <= 57) {
				h = h - 48;
			} else {
				h = h - 97 + 10;
			}
			if (48 <= l && l <= 57) {
				l = l - 48;
			} else {
				l = l - 97 + 10;
			}
			hexBuffer[i] = h * 16 + l;
		}
		return hexBuffer;
	}
	// 16进制字符串转成array buffer
	hexToBytes(str) {
		const typedArray = new Uint8Array(str.match(/[\da-f]{2}/gi).map((h) => {
			return parseInt(h, 16)
		}))
		return typedArray.buffer
	}
	// 16进制转字符串
	ab2hex(buffer) {
		const hexArr = Array.prototype.map.call(
			new Uint8Array(buffer),
			function(bit) {
				return (`00${bit.toString(16)}`).slice(-2)
			}
		)
		return hexArr.join('')
	}
	// DFU升级中,蓝牙设备返回数据的解析
	parseResponse(response) {
		var response = new DataView(response);
		const responseCode = response.getUint8(0); // 返回数据第一位:response code  60:表示成功
		const responseOpCode = response.getUint8(1); // 第二位 是操作命令  也就是响应的是那条命令的操作
		const resultCode = response.getUint8(2); // 第三位 指令的执行结果 01表示成功
		let responseSpecificData;
		if (responseCode !== 0x60) {
			throw new Error({
				errCode: 99994
			});
		}
		if (resultCode !== 0x01) {
			throw new Error({
				errCode: 99995
			});
		}
		switch (responseOpCode) {
			case 0x01:
				break;
			case 0x02:
				break;
			case 0x03: //3
				responseSpecificData = {
					offset: littleEndianUtils.littleEndianUInt32(response.getUint32(CALCULATE_CHECKSUM_RESPONSE_FIELD.OFFSET)),
					crc32: littleEndianUtils.littleEndianUInt32(response.getUint32(CALCULATE_CHECKSUM_RESPONSE_FIELD.CRC32)),
				};
				break;
			case 0x04:
				break;
			case 0x06: // 6
				responseSpecificData = {
					maximumSize: littleEndianUtils.littleEndianUInt32(response.getUint32(
						SELECT_RESPONSE_FIELD.MAXIMUM_SIZE)),
					offset: littleEndianUtils.littleEndianUInt32(response.getUint32(SELECT_RESPONSE_FIELD
						.OFFSET)),
					crc32: littleEndianUtils.littleEndianUInt32(response.getUint32(SELECT_RESPONSE_FIELD
						.CRC32)),
				};
				break;
			default:
				throw new Error({
					errCode: 99996
				});
		}
		return {
			responseCode: responseCode,
			responseOpCode: responseOpCode,
			resultCode: resultCode,
			data: responseSpecificData,
		};
	}
	formater4Length(data) {
		data = data.toString(16)
		if (data.length == 3) {
			data = "0" + data
		} else if (data.length == 2) {
			data = "00" + data
		} else if (data.length == 1) {
			data = "000" + data
		}
		const dataArray = new Array();
		dataArray.push("0x" + data.slice(2))
		dataArray.push("0x" + data.slice(0, 2))
		console.log("formater4Length hexstr:" + dataArray)
		return dataArray;
	}
}
export default new BleUtils();